package ru.eas.tetris;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import ru.eas.tetris.tetromino.Tetromino;

import static ru.eas.tetris.TetrisTheGame.*;

/**
 * @author aesipov
 */
public class GameView
{
    public static final int  SECOND               = 1000;
    public static final int  WINDOW_WIDTH         = 500;
    public static final int  WINDOW_HEIGHT        = 700;
    private int              UPDATE_SCREEN_PERIOD = 50;

    private static final int CELL_SIZE            = 30;
    private static final int WELL_X               = 1;
    private static final int WELL_Y               = 1;
    private static final int NEXT_FIGURE_X        = 12;
    private static final int NEXT_FIGURE_Y        = 1;

    private TetrisTheGame    game;
    private final DrawPanel  drawPanel;
    private int              fps;
    private int              frameCount;

    private Graphics         g;

    public GameView(final TetrisTheGame game)
    {
        this.game = game;

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setSize(new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT));
        frame.setContentPane(drawPanel = new DrawPanel());
        frame.addKeyListener(new KeyAdapter()
        {
            @Override
            public void keyPressed(KeyEvent e)
            {
                if (e.getKeyCode() == KeyEvent.VK_UP)
                {
                    game.rotateLeft();
                }
                if (e.getKeyCode() == KeyEvent.VK_LEFT)
                {
                    game.moveLeft();
                }
                if (e.getKeyCode() == KeyEvent.VK_RIGHT)
                {
                    game.moveRight();
                }
                if (e.getKeyCode() == KeyEvent.VK_DOWN)
                {
                    game.moveDown();
                }
            }
        });
        Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
        int x = (int) ((dimension.getWidth() - frame.getWidth()) / 2);
        int y = (int) ((dimension.getHeight() - frame.getHeight()) / 2);
        frame.setLocation(x, y);

        frame.setVisible(true);

        Thread thread = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                while (true)
                {
                    updateScreen();
                    sleep(UPDATE_SCREEN_PERIOD);
                }
            }
        });
        thread.start();

        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                while (true)
                {
                    fps = frameCount;
                    frameCount = 0;
                    sleep(SECOND);
                }
            }
        }).start();
    }

    class DrawPanel extends JPanel
    {
        @Override
        public void paint(Graphics g)
        {
            super.paint(g);
            GameView.this.g = g;
            drawGame();
            // drawFPS(12, 7);
            frameCount++;
        }
    }

    private void drawGame() {
        GameState state = game.getState();

        if (state == GameState.READY_TO_START) {
            return;
        }

        drawBackground();
        drawWell();
        drawFigure(game.getNextFigure(), NEXT_FIGURE_X, NEXT_FIGURE_Y);

        drawLevel(12, 7);
        drawNextLevelScores(12, 8);
        drawScores(12, 9);

        switch (state) {
            case STARTED:
                drawFigure();
                break;
            case REMOVE_LINES:
                drawWellCompleteLines();
                break;
            case SHOW_LEVEL:
                drawShowLevel();
                break;
            case LEVEL_DONE:
                drawLevelDone();
                break;
            case GAME_OVER:
                drawGameOver();
                break;
        }
    }

    private void drawLevel(int x, int y) {
        g.drawString("Level: " + game.getLevel(), CELL_SIZE * x, CELL_SIZE * y);
    }

    private void drawNextLevelScores(int x, int y) {
        g.drawString("Next level scores: " + game.getNextLevelScores(), CELL_SIZE * x, CELL_SIZE * y);
    }

    private void drawGameOver() {

        fillRect(2, 6, 8, 6, Color.LIGHT_GRAY);
        drawRect(2, 6, 8, 6, Color.BLACK);
        drawString("Game Over", 4, 9, Color.BLACK);
    }

    private void drawShowLevel() {

        fillRect(2, 6, 8, 6, Color.LIGHT_GRAY);
        drawRect(2, 6, 8, 6, Color.BLACK);
        drawString("Level " + game.getLevel(), 4, 9, Color.BLACK);
    }

    private void drawLevelDone() {

        fillRect(2, 6, 8, 6, Color.LIGHT_GRAY);
        drawRect(2, 6, 8, 6, Color.BLACK);
        drawString("Level " + game.getLevel() + " is done", 4, 9, Color.BLACK);
    }

    private void drawString(String text, int x, int y, Color color) {
        g.setFont(new Font("Arial", Font.BOLD, 20));
        g.setColor(color);
        g.drawString(text, CELL_SIZE * x, CELL_SIZE * y);
    }

    private void drawScores(int x,
                            int y)
    {
        g.drawString("Scores: " + game.getScores(), CELL_SIZE * x, CELL_SIZE * y);
    }

    private void drawWell()
    {
        Well well = game.getWell();
        fillRect(WELL_X, WELL_Y, well.getWidth(), well.getHeight(), Color.CYAN);
        drawRect(WELL_X, WELL_Y, well.getWidth(), well.getHeight(), Color.BLACK);
        drawGrid(WELL_X, WELL_Y, well.getWidth(), well.getHeight(), Color.BLACK);

        for (int x = 0; x < well.getWidth(); x++)
        {
            for (int y = 0; y < well.getHeight(); y++)
            {
                if (well.isSquare(x, y))
                {
                    drawSquare(WELL_X + x, WELL_Y + y, well.getColor(x, y));
                }
            }
        }
    }

    private void drawWellCompleteLines()
    {
        Well well = game.getWell();
        for (int lineNumber : game.getCompleteLines())
        {
            fillRect(WELL_X, WELL_Y + lineNumber, well.getWidth(), 1, Color.RED);
        }
    }

    private void sleep(int millis)
    {
        try
        {
            Thread.sleep(millis);
        }
        catch (InterruptedException e)
        {
            throw new IllegalStateException(e);
        }
    }

    private void updateScreen()
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                drawPanel.repaint();
            }
        });
    }

    private void drawBackground()
    {

    }

    private void drawFPS(int x,
                         int y)
    {
        g.drawString("fps: " + fps, CELL_SIZE * x, CELL_SIZE * y);
    }

    private void drawFigure()
    {
        Figure figure = game.getFigure();
        int x0 = WELL_X + figure.getX();
        int y0 = WELL_Y + figure.getY();
        drawFigure(figure, x0, y0);
    }

    private void drawFigure(Figure figure,
                            int x0,
                            int y0)
    {
        Tetromino t = figure.getTetromino();
        for (int x = 0; x < t.getWidth(); x++)
        {
            for (int y = 0; y < t.getHeight(); y++)
            {
                if (t.isSquare(x, y))
                {
                    drawSquare(x0 + x, y0 + y, figure.getColor());
                }
            }
        }
    }

    private void drawGrid(int x,
                          int y,
                          int width,
                          int height,
                          Color color)
    {
        g.setColor(color);
        int x0 = x * CELL_SIZE;
        int y0 = y * CELL_SIZE;
        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                g.drawLine(x0 + i * CELL_SIZE, y0 + j * CELL_SIZE, x0 + i * CELL_SIZE, y0 + j * CELL_SIZE);
            }
        }
    }

    private void fillRect(int x,
                          int y,
                          int width,
                          int height,
                          Color color)
    {
        g.setColor(color);
        x = x * CELL_SIZE;
        y = y * CELL_SIZE;
        g.fillRect(x, y, width * CELL_SIZE, height * CELL_SIZE);
    }

    private void drawLine(int x1,
                          int y1,
                          int x2,
                          int y2,
                          Color color)
    {
        g.setColor(color);
        g.drawLine(x1 * CELL_SIZE, y1 * CELL_SIZE, x2 * CELL_SIZE, y2 * CELL_SIZE);
    }

    private void drawSquare(int x,
                            int y,
                            Color color)
    {
        g.setColor(color);
        g.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
        g.setColor(Color.BLACK);
        g.drawRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
    }

    private void drawRect(int x,
                          int y,
                          int width,
                          int height,
                          Color color)
    {
        g.setColor(color);
        x = x * CELL_SIZE;
        y = y * CELL_SIZE;
        g.drawRect(x, y, width * CELL_SIZE, height * CELL_SIZE);
    }
}
